package com.gorkemozbek.rubic.util

import scala.collection.{Iterator, Set}

/*
 * A counter based on a hash table of counts.
 */
class CountTable[E] extends Counter[E] {

  protected class Count(initialCount: Int) {
    def this() = this(0)
    private[this] var c: Int = initialCount
    def set(count: Int): Unit = { c = count }
    def get: Int = c
  }

  protected val table = scala.collection.mutable.HashMap[E, Count]()

  protected def updateCount(elem: E, cnt: Int): Unit = table getOrElseUpdate(elem, new Count) set(cnt)

  def counts: Iterator[(E, Int)] = table.iterator map { cnt => (cnt._1, cnt._2.get) }

  def remove(elem: E): Boolean = table remove(elem) match { case Some(_) => true; case None => false }

  override def clear: Unit = table clear

  override def getCount(elem: E): Int = table get(elem) match { case Some(x) => x.get; case None => 0 }

  override def elements: Set[E] = table.keySet

  protected def incrementCount(cnt: Count, inc: Int): Unit = cnt set(cnt.get + inc)

  override  def increment(elem: E, inc: Int = 1): Unit = table.get(elem) match {
    case Some(x) => incrementCount(x, inc)
    case None => table update(elem, new Count(inc))
  }
  
  override def incrementAll(inc: Int = 1): Unit = table foreach { cnt => incrementCount(cnt._2, inc) }

  protected def decrementCount(cnt: Count, dec: Int): Unit = cnt set(nonNegativeInt(cnt.get - dec))

  override def decrement(elem: E, dec: Int = 1): Unit = table get(elem) match {
    case Some(x) => decrementCount(x, dec)
    case None => table update(elem, new Count)
  }

  override def decrementAll(dec: Int = 1): Unit = table foreach { cnt => decrementCount(cnt._2, dec) }

  override def contains(elem: E): Boolean = table contains(elem)

  override def size: Int = table size

  override def isEmpty: Boolean = table isEmpty

}
